/*----------------------------------------------------------------------
 *
 * Confidential and Proprietary to Taggle Systems / Widentifi Pty. Ltd.
 *
 *     Copyright 2009, Widentifi Pty. Ltd.  All rights reserved.
 *
 * This precautionary copyright notice against inadvertent publication
 * is neither an acknowledgement of publication, nor a waiver of
 * confidentiality.
 *
 *---------------------------------------------------------------------
 *
 * Very simple module to receive the lpcb bus across chip boundries.
 *
 *---------------------------------------------------------------------
 * $Id: lpcb_rx.v 667 2010-07-01 00:12:17Z jayshurtz $
 * $URL: http://hatch.googlecode.com/svn/tags/taggle_release_2.2/examples/modules/dcom_lpcb/lpcb_rx.v $
 * $Author: jayshurtz $
 *---------------------------------------------------------------------*/

`include "lpcb_defines.vh"

module lpcb_rx

  (/*AUTOARG*/
  // Outputs
  lpcb_frame_out, lpcb_cad_out,
  // Inputs
  clk, reset, lpcb_rx_strobe, lpcb_rx_frame, lpcb_rx_cad
  );

  //----------------------------------------
  input                 clk;
  input                 reset;
  //----------------------------------------

  //----------------------------------------
  // lpcb interface
  input                 lpcb_rx_strobe;
  input                 lpcb_rx_frame;
  input [3:0]           lpcb_rx_cad;

  output                lpcb_frame_out;
  output [3:0]          lpcb_cad_out;
  //----------------------------------------


  /*----------------------------------------------------------------*/

  /*-AUTOUNUSED-*/

  /*AUTOINPUT*/

  /*AUTOOUTPUT*/

  /*-AUTOREGINPUT-*/

  /*AUTOREG*/

  /*AUTOWIRE*/

  /*------------------------------------------------------------------
   *
   * local definitions and connections.
   *
   * */

  /*------------------------------------------------------------------
   *
   * synchronise the strobe signal to generate a load enable.
   *
   * */

  reg [2:0]             lpcb_rx_strobe_sync;
  wire                  lpcb_rx_strobe_re;
  always @ (posedge clk)
    lpcb_rx_strobe_sync <= {lpcb_rx_strobe_sync[1:0], lpcb_rx_strobe};

  assign                lpcb_rx_strobe_re = lpcb_rx_strobe_sync[1] & ~lpcb_rx_strobe_sync[2];

  /*------------------------------------------------------------------
   *
   * build a fifo.
   *
   * */

  reg [3:0]             cad [0:15];
  reg                   frame [0:15];
  reg [3:0]             write_pointer;
  reg                   force_go;
  always @ (posedge clk) begin
    if (reset) begin
      write_pointer <= 4'd0;
      cad[write_pointer]   <= 4'd0;
      frame[write_pointer] <= 1'd0;
      force_go <= 1'd0;
    end else begin
      cad[write_pointer]   <= lpcb_rx_cad;
      frame[write_pointer] <= lpcb_rx_frame;
      if (lpcb_rx_strobe_re) begin
        write_pointer <= write_pointer + 4'd1;
        force_go <= lpcb_rx_frame;
      end else begin
        write_pointer <= write_pointer;
        force_go <= 1'd0;
      end
    end
  end

  /*-----------------------------------------------------------------
   *
   * only dump the fifo contents when we have enaugh data in it to
   * match the command or another command was dumped into the fifo
   * over writing the current command.
   *
   * */

  reg [3:0] read_pointer;
  reg [4:0] count;
  reg       lpcb_frame_out;
  reg [3:0] lpcb_cad_out;
  reg       drop_cmd;
  reg       run_cmd;

  wire [3:0] depth;
  assign     depth = (write_pointer >= read_pointer) ?
                     write_pointer - read_pointer :
                     write_pointer + (5'd16 - read_pointer);
  always @ (posedge clk) begin
    if (reset) begin
      read_pointer   <= 4'd0;
      count          <= 5'd0;
      drop_cmd       <= 1'd0;
      run_cmd        <= 1'd0;
      lpcb_frame_out <= 1'd0;
      lpcb_cad_out   <= 4'd0;
    end else begin
      if (drop_cmd) begin
        // drop the incomplete command.
        lpcb_frame_out <= 1'd0;
        lpcb_cad_out   <= 4'd0;
        run_cmd        <= 1'd0;
        count          <= 5'd0;
        if (frame[read_pointer]) begin
          // we've found the start of a new command.
          drop_cmd       <= 1'd0;
        end else begin
          drop_cmd       <= 1'd1;
          read_pointer   <= read_pointer + 4'd1;
        end
      end else if ((run_cmd == 1'd0) && (count > 5'd0)) begin
        // dump this command from the fifo onto the bus.
        run_cmd        <= 1'd0;
        if (frame[read_pointer]) begin
          // unexpectedly found a new command.... eek!
          lpcb_frame_out <= 1'd0;
          lpcb_cad_out   <= 4'd0;
          count          <= 5'd0;
        end else begin
          lpcb_frame_out <= 1'd0;
          lpcb_cad_out   <= cad[read_pointer];
          read_pointer   <= (count > 5'd1) ? read_pointer + 4'd1 : read_pointer;
          count          <= count - 5'd1;
        end
      end else if (write_pointer != read_pointer) begin
        if (force_go) begin
          drop_cmd       <= 1'd1;
          run_cmd        <= 1'd0;
          lpcb_cad_out   <= 4'd0;
          count          <= 5'd0;
        end else begin
          drop_cmd       <= 1'd0;
          lpcb_cad_out   <= cad[read_pointer];
          if (run_cmd) begin
            // this mini pipeline stage is in here to help meet timing.
            read_pointer   <= read_pointer + 4'd1;
            lpcb_frame_out <= 1'd1;
            run_cmd        <= 1'd0;
            count          <= count;
          end else if (frame[read_pointer]) begin
            // we have a framed command.
            lpcb_frame_out <= 1'd0;
            count          <= 5'd0;
            case(cad[read_pointer])
              `LPCB_WRITE_8B_CMD  :
                if (depth >= 5'd11) begin  // cmd + 2 addr + 8 data
                  count   <= 5'd11;
                  run_cmd <= 1'd1;
                end
              `LPCB_WRITE_16B_CMD :
                if (depth >= 5'd13) begin // cmd + 4 addr + 8 data
                  count <= 5'd13;
                  run_cmd <= 1'd1;
                end
              `LPCB_WRITE_24B_CMD :
                if (depth >= 5'd15) begin // cmd + 6 addr + 8 data
                  count <= 5'd15;
                  run_cmd <= 1'd1;
                end
              `LPCB_WRITE_32B_CMD :
                if (depth >= 5'd17) begin // cmd + 8 addr + 8 data
                  count <= 5'd17;
                  run_cmd <= 1'd1;
                end
              `LPCB_READ_8B_CMD :
                if (depth >= 5'd3) begin  // cmd + 2 addr
                  count <= 5'd3;
                  run_cmd <= 1'd1;
                end
              `LPCB_READ_16B_CMD :
                if (depth >= 5'd5) begin  // cmd + 4 addr
                  count <= 5'd5;
                  run_cmd <= 1'd1;
                end
              `LPCB_READ_24B_CMD :
                if (depth >= 5'd7) begin  // cmd + 6 addr
                  count <= 5'd7;
                  run_cmd <= 1'd1;
                end
              `LPCB_READ_32B_CMD :
                if (depth >= 5'd9) begin  // cmd + 8 addr
                  count <= 5'd9;
                  run_cmd <= 1'd1;
                end
              `LPCB_READ_ACK_CMD :
                if (depth >= 5'd9) begin  // cmd + 8 data
                  count <= 5'd9;
                  run_cmd <= 1'd1;
                end
              default             : begin
                // unrecognised command.
                count    <= 5'd0;
                drop_cmd <= 1'd1;
                run_cmd  <= 1'd0;
              end
            endcase // case(lpcb_cad_a_int)
          end else begin // if (frame[read_pointer])
            // we are looking for a copmmand that is not framed!  bail.
            count    <= 5'd0;
            drop_cmd <= 1'd1;
            run_cmd  <= 1'd0;
          end // else: !if(frame[read_pointer])
        end // else: !if(force_go)
      end // if (write_pointer != read_pointer)
    end // else: !if(reset)
  end // always @ (posedge clk)

  /*----------------------------------------------------------------*/

endmodule // lpcb_rx


// Local Variables:
// verilog-library-directories:(".")
// verilog-library-extensions:(".v")
// End:
